package api

import (
	"errors"
	"log"
	"mime/multipart"
	"strconv"

	"github.com/EDDYCJY/go-gin-example/models"
	"github.com/EDDYCJY/go-gin-example/pkg/app"
	"github.com/EDDYCJY/go-gin-example/pkg/e"
	"github.com/EDDYCJY/go-gin-example/pkg/logging"
	"github.com/EDDYCJY/go-gin-example/pkg/uploadfile"

	"github.com/gin-gonic/gin"
)

// const default_upload_file_path_temp = "D:\\coding\\go\\go\\webserver\\temp"
// const default_upload_file_path = "D:\\coding\\go\\go\\webserver\\uploads"

const default_upload_file_path_temp = "/data/download/temp/"
const default_upload_file_path = "/data/download/ai_dataset/"

func validateParams(c *gin.Context) (*multipart.FileHeader, int, error) {
	file, err := c.FormFile("file")
	if err != nil {
		return nil, 0, errors.New("请求参数错误,上传文件 " + err.Error())
	}
	if file.Size <= 0 {
		return nil, 0, errors.New("请求参数错误,不能上传空文件 " + err.Error())
	}

	chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex"))
	if err != nil {
		return nil, 0, errors.New("请求参数错误,请填写chunkIndex " + err.Error())
	}

	return file, chunkIndex, nil
}

func Handle_File_Upload(c *gin.Context) {
	appG := app.Gin{C: c}

	//验证参数
	file, chunkIndex, err := validateParams(c)
	if err != nil {
		appG.ResponseError(e.ERROR, err.Error())
		return
	}

	//验证文件是否存在
	if checkFileExistAndIsFirstChunk(file.Filename, chunkIndex) {
		appG.ResponseError(e.ERROR, "文件已经存在了")
		return
	}

	// 文件上传
	fileReader, err := file.Open()
	if err != nil {
		appG.ResponseError(e.ERROR, "请求参数错误,文件无法打开")
		logging.Error(err)
		return
	}
	defer fileReader.Close()

	// 文件保存
	tempFilePath, err := uploadfile.SaveChunkToFile(fileReader, chunkIndex, default_upload_file_path_temp, file.Filename)
	if err != nil {
		appG.ResponseError(e.ERROR, "文件保存失败"+err.Error())
		return
	}

	appG.ResponseSuccess(tempFilePath)
}

func checkFileExistAndIsFirstChunk(fileName string, chunkIndex int) bool {
	if chunkIndex != 0 {
		return false
	}

	exists, _ := models.GetFileByFileName(fileName)
	return exists != nil
}

type MergeFileParams struct {
	FileName     string `form:"fileName" binding:"required"`
	TotalChunks  int    `form:"totalChunks" binding:"required"`
	UserID       int    `form:"userId" binding:"required"`
	MainCategory string `form:"mainCategory,default=other"`
	SubCategory  string `form:"subCategory,default=other"`
	Description  string `form:"description"`
	ImageURL     string `form:"imageUrl"`
	DataCategory int    `form:"dataCategory,default=0"`
	FileSize     uint   `form:"fileSize,default=0"`
}

// Handle_Merge_File 处理文件合并请求
func Handle_Merge_File(c *gin.Context) {
	appG := app.Gin{C: c}

	// 获取请求参数
	var params MergeFileParams
	if err := c.ShouldBind(&params); err != nil {
		appG.ResponseError(e.ERROR, "请求参数错误："+err.Error())
		return
	}

	//合并文件
	destinationFilePath, fileSize, err := uploadfile.MergeChunks(default_upload_file_path_temp, default_upload_file_path, params.FileName, params.TotalChunks)

	if err != nil {
		appG.ResponseError(e.ERROR, "文件合并失败"+err.Error())
		return
	}

	//获取文件尺寸
	if params.FileSize == 0 {
		params.FileSize = fileSize
	}

	// 保存文件信息到数据库
	file, err := saveFileToDatabase(params, destinationFilePath)
	if err != nil {
		appG.ResponseError(e.ERROR, "数据库文件创建失败"+err.Error())
		logging.Error(err)
		return
	}

	// 删除临时文件
	go func() {
		err = uploadfile.DeleteChunks(default_upload_file_path_temp, params.FileName, params.TotalChunks)
		if err != nil {
			log.Println("删除临时文件失败: ", err)
			logging.Error(err)
		}
	}()

	appG.ResponseSuccess(file)
}

// saveFileToDatabase 保存文件信息到数据库
func saveFileToDatabase(params MergeFileParams, destinationFilePath string) (*models.File, error) {
	file := models.File{
		Name:      params.FileName,
		UserID:    uint(params.UserID),
		Favourite: 0,
		Freeze:    0,
		FilePath:  destinationFilePath,

		FileSizeBytes: params.FileSize,
		DataCategory:  uint(params.DataCategory),
		MainCategory:  params.MainCategory,
		SubCategory:   params.SubCategory,
		Description:   params.Description,
		ImageURL:      params.ImageURL,
	}
	err := models.CreateFile(&file)
	if err != nil {
		return nil, err
	}
	return &file, nil
}

// Handle_Get_File_List 处理 GET 请求以检索文件列表。
// 它返回一个成功响应，其中包含文件列表和总文件数。
func Handle_Get_File_List(c *gin.Context) {
	appG := app.Gin{C: c}

	pageSize, pageNum, err := getPaginationParams(c)
	if err != nil {
		appG.ResponseError(e.ERROR, "请求参数错误,请填写正确的分页类别")
		return
	}

	file := &models.File{
		DataCategory: getDataCategory(c),
		MainCategory: c.DefaultQuery("mainCategory", ""),
		SubCategory:  c.DefaultQuery("subCategory", ""),
	}
	searchTerm := &models.SearchTerm{
		Description: c.DefaultQuery("description", ""),
	}

	files, total, err := getFileList(file, searchTerm, pageNum, pageSize)
	if err != nil {
		appG.ResponseError(e.ERROR, "查询文件失败"+err.Error())
		return
	}

	appG.ResponseSuccess(gin.H{"files": files, "total": total})
}

// getPaginationParams 用于获取分页参数
// 参数：
// - c: gin.Context 对象
// 返回值：
// - int: 每页显示的数量
// - int: 当前页码
// - error: 错误信息
func getPaginationParams(c *gin.Context) (int, int, error) {
	pageSize, errP := strconv.Atoi(c.DefaultQuery("pageSize", "10")) // 获取每页显示的数量，默认为10
	pageNum, errN := strconv.Atoi(c.DefaultQuery("pageNum", "1"))    // 获取当前页码，默认为1
	if errP != nil || errN != nil {
		return 10, 1, errors.New("invalid pagination params")
	}
	return pageSize, pageNum, nil
}

// 获取数据类别
func getDataCategory(c *gin.Context) uint {
	dataCategory, err := strconv.Atoi(c.DefaultQuery("dataCategory", "0"))
	if err != nil {
		return 0
	}
	return uint(dataCategory)
}

// getFileList 用于获取符合条件的文件列表和总数
// 参数：
// - file: 查询条件
// - searchTerm: 搜索条件
// - pageNum: 当前页码
// - pageSize: 每页显示的数量
// 返回值：
// - []*models.File: 符合条件的文件列表
// - int: 符合条件的文件总数
// - error: 错误信息
func getFileList(file *models.File, searchTerm *models.SearchTerm, pageNum, pageSize int) ([]*models.File, int, error) {
	total, err := models.GetFileListCount(file, searchTerm) // 获取符合条件的文件总数
	if err != nil {
		return nil, 0, err
	}
	files, err := models.GetFileList(file, searchTerm, pageNum, pageSize) // 获取符合条件的文件列表
	if err != nil {
		return nil, 0, err
	}

	return files, total, nil
}
